use chat_core::{Chat, ChatType};
use utoipa::ToSchema;
use serde::{Serialize,Deserialize};

use crate::{AppState, error::AppError};



#[derive(Debug,Serialize,Deserialize,Clone,ToSchema,Default)]
pub struct CreateChat{
    pub name: Option<String>,
    pub members: Vec<i64>,
    pub public: bool,
}

impl AppState {
    pub async fn create_chat(
        &self,
        inoput: CreateChat,
        user_id: u64,
        ws_id: u64
    ) -> Result<Chat,AppError> {
        let len = inoput.members.len();
        if len < 2 {
            return Err(AppError::CreateChatError("Chat must have at least 2 members".to_string()));

        }
        if  !inoput.members.contains(&(user_id as i64)) {
            return Err(AppError::CreateChatError("You must be a member of the Chat".to_string()));
        }

        if let Some(name) = &inoput.name {
            if name.len() < 3 {
                return Err(AppError::CreateChatError("Chat name must be at least 3 characters long".to_string()));
            }
        }

        if len > 8 && inoput.name.is_none() {
            return Err(AppError::CreateChatError("Group Chat with more than 8 members must have a name".to_string()));
        }

        let chat_type = match (&inoput.name,len) {
            (None,2) => ChatType::Single,
            (None,_) => ChatType::Group,
            (Some(_),_) => {
                if inoput.public {
                    ChatType::PublicChannel
                } else {
                    ChatType::PrivateChannel
                }
            }
        };

        let chat = sqlx::query_as(
            "
            INSERT INTO chats (ws_id,name,type,members)
            VALUES ($1,$2,$3,$4)
            RETURNING id,ws_id,name,type,members,created_at
            ",
        )
        .bind(ws_id as i64)
        .bind(inoput.name)
        .bind(chat_type)
        .bind(inoput.members)
        .fetch_one(&self.pool)
        .await?;

        Ok(chat)
    }

    pub async fn fetch_chats(&self,user_id: u64,ws_id: u64) -> Result<Vec<Chat>,AppError> {
        let chats = sqlx::query_as(
            "SELECT id,ws_id,name,type,members,created_at
            FROM chats
            WHERE ws_id = $1 AND $2 = ANY(members)"
        )
        .bind(ws_id as i64)
        .bind(user_id as i64)
        .fetch_all(&self.pool)
        .await?;
        Ok(chats)
    }

    pub async fn get_chat_by_id(&self,id: u64) ->Result<Option<Chat>,AppError> {
        let chat = sqlx::query_as(
            "SELECT iu,ws_id,name,type,members,created_at 
            FROM chats WHERE id = $1"
        )
        .bind(id as i64)
        .fetch_optional(&self.pool)
        .await?;
    Ok(chat)
    }

    pub async fn is_chat_members(&self,chat_id: u64,user_id: u64) ->Result<bool,AppError>{
        let is_member = sqlx::query(
            "
                SELECT 1 FROM chats
                WHERE id = $1 AND $2 = ANY(members)
            "
        )
        .bind(chat_id as i64)
        .bind(user_id as i64)
        .fetch_optional(&self.pool)
        .await?;
        Ok(is_member.is_some())
    }

}


#[cfg(test)]
impl CreateChat {
    pub fn new(name: &str,members: &[i64],public: bool) -> Self {
        let name = if name.is_empty() {
            None
        } else {
            Some(name.to_string())
        };
        Self {
            name,
            members: members.to_vec(),
            public,
        }
    }
}

#[cfg(test)]
mod tests {

    // use super::*;
    // use anyhow::Result;
    
    // #[tokio::test]
    // async fn create_single_chat_test() -> Result<()> {
    //     let pool_url = "postgres://postgres:postgres@117.72.59.133:5432/chat";
    //     let pool = sqlx::PgPool::connect(pool_url).await?;
    //     let cchat = CreateChat::new("Test Chat", &[1, 2], false);
    //     let chat = AppState::create_chat(
    //         &pool,
    //         cchat,
    //         1,
    //         1
    //     ).await?;
    //     assert_eq!(chat.name, Some("Test Chat".into()));
    //     assert_eq!(chat.members, vec![1, 2]);
    //     Ok(())
    // }

}